using System;
using System.ComponentModel;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using WebPart = System.Web.UI.WebControls.WebParts.WebPart;

namespace SDSFileUpload.SDSUpload
{
    /*
     * 
     * Strategic Data Systems
     * 10785 Yankee Street
     * Dayton, OH 45458
     * PH: 937-886-9405 
     * http://www.sds-consulting.com
     * http://sdssharepointlibrary.codeplex.com
     * 
     */

    /// <summary>
    /// Upload Web part
    /// </summary>
    [ToolboxItemAttribute(false)]
    public class SDSUploadWebPart : WebPart
    {
        /// <summary>
        /// Field for messages from web part
        /// </summary>
        protected Label lblMessage = new Label();
        /// <summary>
        /// Label for Destination field
        /// </summary>
        protected Label lblDestination = new Label();
        /// <summary>
        /// Dropdown list for destinations
        /// </summary>
        protected DropDownList ddlDestinations = new DropDownList();
        /// <summary>
        /// File upload field
        /// </summary>
        protected System.Web.UI.WebControls.FileUpload file = new System.Web.UI.WebControls.FileUpload();
        /// <summary>
        /// Button to process upload
        /// </summary>
        protected Button btnSubmit = new Button { Text = "Submit" };

        /// <summary>
        /// Name of SharePoint List used to get configuration information
        /// </summary>
        [WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        Category("Configuration"),
        WebDisplayName("Configuration List Name"),
        WebDescription("Name of the configuration list")]
        public string ConfigurationListName { get; set; }

        /// <summary>
        /// SharePoint List that contains the configuration data
        /// </summary>
        private SPList ConfigurationList
        {
            get
            {
                SPList outval = null;
                SPSecurity.RunWithElevatedPrivileges(() =>
                {
                    using (SPSite tSite = new SPSite(SPContext.Current.Site.ID))
                    {
                        using (SPWeb tWeb = tSite.AllWebs[SPContext.Current.Web.ID])
                        {
                            outval = tWeb.Lists[this.ConfigurationListName];
                        }
                    }
                });

                return outval;
            }
        }

        /// <summary>
        /// Title field: the text to show in the drop down list
        /// </summary>
        [WebBrowsable(true), 
        Personalizable(PersonalizationScope.Shared),
        Category("Configuration"), 
        WebDisplayName("Title Field"),
        WebDescription("InternalName of the Field for the title to be displayed"),
        DefaultValue("Title")]
        public string TitleField { get; set; }

        /// <summary>
        /// Destination field: the SP Document Library
        /// </summary>
        [WebBrowsable(true), 
        Personalizable(PersonalizationScope.Shared),
        Category("Configuration"), 
        WebDisplayName("Destination URL Field"),
        WebDescription("InternalName of the Field for the destination URL")]
        public string DestinationUrlField { get; set; }

        /// <summary>
        /// Format field: the format string for naming the uploaded file
        /// </summary>
        [WebBrowsable(true), 
        Personalizable(PersonalizationScope.Shared),
        Category("Configuration"), 
        WebDisplayName("Filename Format Field"),
        WebDescription("InternalName of the Field for the Filename Format String")]
        public string FilenameFormatField { get; set; }

        /// <summary>
        /// Constructor of this web part class
        /// </summary>
        public SDSUploadWebPart()
        {
            // Page (WebPart) Events
            this.PreRender += SDSUpload_PreRender;

            // Control Events
            this.btnSubmit.Click += btnSubmit_Click;
        }

        /// <summary>
        /// Create the web controls used by this web part.
        /// </summary>
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            this.Controls.Add(lblMessage);
            this.Controls.Add(lblDestination);
            this.Controls.Add(new Literal { Text = ": " });
            this.Controls.Add(ddlDestinations);
            this.Controls.Add(new Literal { Text = " " });
            this.Controls.Add(file);
            this.Controls.Add(new Literal { Text = " " });
            this.Controls.Add(btnSubmit);
        }

        /// <summary>
        /// PreRender event used to get Configuration list and populate dropdown list of possible destinations
        /// </summary>
        /// <param name="sender">Standard sender object</param>
        /// <param name="e">Standard EventArgs</param>
        protected void SDSUpload_PreRender(object sender, System.EventArgs e)
        {
            // if postback, do nothing
            if (Page.IsPostBack)
                return;

            try
            {
                lblMessage.Text = String.Empty;

                SPList tList = ConfigurationList;

                // set the title of the destinations
                lblDestination.Text = tList.Fields.Cast<SPField>().Single(x => x.InternalName == TitleField).Title;

                // clear the current list of destinations
                ddlDestinations.Items.Clear();
                // Add the destinations by getting the list items' title field value
                ddlDestinations.Items.AddRange(tList.GetItems(new string[] { TitleField })
                                                    .Cast<SPListItem>()
                                                    .Select(item => new ListItem(item[TitleField].ToString(),
                                                                                 item.ID.ToString()))
                                                    .ToArray());
            } // try
            catch (Exception ex)
            {
                //  Build meaniful error message when an exception occurs
                string errorMessage = GetErrorMessage(ex);

                this.lblMessage.Text = "<span style='color:red'>" + errorMessage + "</span><br/>";

                this.lblDestination.Visible = false;
                this.ddlDestinations.Visible = false;
                this.file.Visible = false;
                this.btnSubmit.Visible = false;
            } // catch
        }

        /// <summary>
        /// Submit button clicked event
        /// </summary>
        /// <param name="sender">Standard sender object</param>
        /// <param name="e">Standard EventArgs</param>
        protected void btnSubmit_Click(object sender, System.EventArgs e)
        {
            try
            {
                this.lblMessage.Text = String.Empty;

                SPListItem tItem = ConfigurationList.Items.GetItemById(int.Parse(ddlDestinations.SelectedValue));

                // obtain the destination URL
                string DestinationUrl = tItem[DestinationUrlField].ToString();

                // upload the file using elevated privileges (act as system)
                SPSecurity.RunWithElevatedPrivileges(() =>
                {
                    using (SPSite tSite = new SPSite(DestinationUrl))
                    {
                        // allow unsafe updates (which allows us to specify user and timestamps manually)
                        tSite.AllowUnsafeUpdates = true;

                        using (SPWeb tWeb = tSite.OpenWeb(DestinationUrl, false))
                        {
                            // allow unsafe updates (which allows us to specify user and timestamps manually)
                            tWeb.AllowUnsafeUpdates = true;

                            // identify the destination folder and filename
                            SPFolder DestinationFolder = tWeb.GetFolder(DestinationUrl);

                            // attempt to add the user to the UserInfo table (does NOT grant permission)
                            SPUser tUser = tWeb.EnsureUser(SPContext.Current.Web.CurrentUser.LoginName);
                            DateTime tNow = DateTime.Now;   // ensure that CreatedOn and ModifiedOn are identical

                            string FilenameFormatString = tItem[FilenameFormatField].ToString();

                            string DestinationFilename = FormatFilename(FilenameFormatString, tUser, tNow, file.FileName);

                            bool validFileToUpload = false;

                            //  Validate file has a valid name
                            if (Microsoft.SharePoint.Utilities.SPUrlUtility.IsLegalFileName(file.FileName))
                            {
                                //  Validate file contains data
                                if (file.PostedFile.ContentLength > 0)
                                {
                                    validFileToUpload = true;
                                }
                                else
                                {
                                    //  File is empty
                                    this.lblMessage.Text = "<span style='color:red'>";
                                    this.lblMessage.Text += "Requested file: " + file.FileName + " does not contain any data and will not be uploaded.  Please specify a file containing data to upload.";
                                    this.lblMessage.Text += "</span><br/>";
                                }
                            }
                            else
                                if (String.IsNullOrEmpty(file.FileName))
                                {
                                    this.lblMessage.Text = "<span style='color:red'>";
                                    this.lblMessage.Text += "Upload file name does not have a file name. Please specify a file to upload.";
                                    this.lblMessage.Text += "</span><br/>";
                                }
                                else if (String.IsNullOrEmpty(file.FileName.Trim()))
                                {
                                    this.lblMessage.Text = "<span style='color:red'>";
                                    this.lblMessage.Text += "Upload file name does not have a file name. Please specify a file to upload.";
                                    this.lblMessage.Text += "</span><br/>";
                                }

                            if (validFileToUpload)
                            {
                                // add using the overload with:
                                //     filename, System.IO.Stream
                                //     CreatedBy, ModifiedBy
                                //     CreatedOn, ModifiedOn
                                DestinationFolder.Files.Add(DestinationFilename,    // filename
                                                            file.FileContent,       // stream
                                                            tUser,                  // created by
                                                            tUser,                  // modified by
                                                            tNow,                   // created on
                                                            tNow);                  // modified on

                                tWeb.AllowUnsafeUpdates = false;

                                this.lblMessage.Text = "<span style='color:blue'>";
                                this.lblMessage.Text += "File: " + HttpUtility.HtmlEncode(file.FileName).ToString() + " has been uploaded.";
                                this.lblMessage.Text += "</span><br/>";

                            } // last else for checking file name

                        } // using tWeb

                        tSite.AllowUnsafeUpdates = false;

                    } // using tSite

                }); // SPSecurity.RunWithElevatedPrivileges

            } // try
                
            catch (Exception ex)
            {
                //  Build meaniful error message when an exception occurs
                string errorMessage = GetErrorMessage(ex);

                this.lblMessage.Text = "<span style='color:red'>" + errorMessage + "</span><br/>";

                this.lblDestination.Visible = false;
                this.ddlDestinations.Visible = false;
                this.file.Visible = false;
                this.btnSubmit.Visible = false;
            } // catch
        }

        /// <summary>
        /// Performs string formatting using the SPUser, DateTime, and FileName
        /// </summary>
        /// <returns>Formatted string with values replaced in the format string</returns>
        /// <remarks>
        /// Supported string replacements and formatting:
        ///     {user:name:X} with SPUser.Name where X is a standard string format 
        ///     {user:email:X} with SPUser.Email where X is a standard string format 
        ///     {datetime:X} where X is a standard datetime format (supported by C#)
        ///     {filename:X} where X is a standard string format
        /// </remarks>
        public static string FormatFilename(string inpFilenameFormat, SPUser inpUser, DateTime dt, string inpFilename)
        {
            char[] chrInvalidCharacters = "'#%&*:<>?\\/{|}~".ToCharArray();

            string User_Name = String.Join(String.Empty, inpUser.Name.Split(chrInvalidCharacters));
            string User_Email = String.Join(String.Empty, inpUser.Email.Split(chrInvalidCharacters));

            string FilenameFormat = inpFilenameFormat.Replace("{filename", "{0")
                                                     .Replace("{datetime", "{1")
                                                     .Replace("{user:name", "{2")
                                                     .Replace("{user:email", "{3");

            return String.Format(FilenameFormat, inpFilename, dt, User_Name, User_Email);
        }

        /// <summary>
        /// Build string with Exception messages
        /// </summary>
        /// <param name="ex">Exception that was thrown</param>
        /// <returns>String with Exception Message and if there is on, the Inner Exception Message</returns>
        private static string GetErrorMessage(Exception ex)
        {
            string errorMessage = "An error has occurred: ";
            if (!String.IsNullOrEmpty(ex.Message))
            {
                //  Add message text from exception
                errorMessage += ex.Message.ToString() + " ";

                //  If exception has an inner exception, then add the inner exception text
                if ((ex.InnerException != null) && (!String.IsNullOrEmpty(ex.InnerException.Message)))
                {
                    errorMessage += " ::" + ex.InnerException.Message.ToString() + " ";
                }
            }
            return errorMessage;
        }

    }
}
